#include <World.h>

#include <Parser.h>

#include <glut.h>
#include <Camera.h>

#include <Area.h>

#include <Area_Alpha.h>
#include <Area_Beta.h>

#include <LoadArea.h>
#include <UnloadArea.h>
#include <EnterMenu.h>

#include <SpaceHUD.h>
#include <LandHUD.h>

World::World(INIFile* configFile)
:config(configFile)
,player(*this)
,mainmenu(*this)
{
	gamefps = 0.016f;
	currentCam = 0;
	currentArea = 0;
	loadingCounter = 0;
	loadingDirection = 0;
	loadingPoint = vec2(100,100);
	loading = 0;
	inmenu = 0;
}

World::~World()
{
	delete currentCam;
}

void World::Initialize()
{
	currentCam = new Camera();
	External::camera = currentCam;

	physics.Init();

	player.Init();

	mainmenu.Load();
	inmenu = 1;

	hudDisplay.AddHUD( "SpaceHUD", new SpaceHUD(*this) );
	hudDisplay.AddHUD( "LandHUD", new LandHUD(*this) );
	hudDisplay.SetActiveHUD( "LandHUD" );

	//TODO: load the menu or first level

	//InitStarsBackground();


	//EnemyShip* sh = CreateGameObject( EnemyShip );
	//sh->SetPosition( vec2(10,10) );

	/*
	player = new Player();
	player->SetWorld( this );
	gameObjects.AddObject( player );
	*/


	/* //TODO: REMOVE THIS IT'S OLDDDDD
	for(unsigned i = 0; i < 10; ++i)
	{
		float fieldSize = 1000.0f;
		float rx = (float)(rand()%10001)/10000.0f;
		float ry = (float)(rand()%10001)/10000.0f;
		b2Vec2 mobPos( rx * fieldSize, ry * fieldSize );
		Monster* newMob = new Monster();
		newMob->position = mobPos;

		gameObjects.AddObject( newMob );
	}

	for(unsigned i = 0; i < 250; ++i)
	{
		float fieldSize = 1000.0f;
		float rx = (float)(rand()%10001)/10000.0f;
		float ry = (float)(rand()%10001)/10000.0f;
		b2Vec2 rockPos( rx * fieldSize, ry * fieldSize );
		Rock* newRock = new Rock();
		newRock->position = rockPos;
		newRock->velocity = b2Vec2( (0.5f - rx) * 0.5f, (0.5f - ry) * 0.5f);

		gameObjects.AddObject( newRock );
	}

	*/
}
		
void World::SetLoading(truth isLoading)
{
	loading = isLoading;
	if( !loading )
	{
		loadingCounter = 0;
		loadingDirection = 0;
		loadingPoint = vec2(100,100);
	}
}
		
void World::SetMainMenu(truth inMainMenu)
{
	inmenu = inMainMenu;
}

void World::UnloadCurrentArea()
{
	if(!currentArea)
		return;

	player.SetControlTarget(0);
	currentArea->Cleanup();
	delete currentArea;
	currentArea = 0;
}
		
void World::SetArea(Area* area)
{
	if(!area)
		return;

	currentArea = area;
}
		
Area* World::GetCurrentArea()
{
	return currentArea;
}

void World::InitCurrentArea()
{
	if(currentArea)
		currentArea->Init();
}
		
void World::Input(const bool* keys, float fps)
{
	if( loading )
		return;

	//special keys go here, these will activate before any others
	if( keys[ 'c' ] )
	{
		Global::SetDebugMode(( Global::DebugState ? 0 : 1 ));
	}

	static truth a = 0;
	static truth b = 0;
	if( keys[ 'f' ] )
	{
		if(!a++)
		{
			AddCommand( new UnloadArea( this ) );
			AddCommand( new LoadArea<Alpha>( this ) );
			b = 0;
			//AddCommand( new ChangeArea(this, new Alpha(*this)) );
		}
	}

	if( keys[ 'g' ] )
	{
		if(!b++)
		{
			AddCommand( new UnloadArea( this ) );
			AddCommand( new LoadArea<Beta>( this ) );
			a = 0;
		}
		//AddCommand( new ChangeArea(this, new Beta(*this)) );
	}

	player.Input( keys, fps );
	currentCam->Input( keys, fps );
}

void World::Update(float fps)
{
	if(loading)
		return;

	if(inmenu)
	{
		mainmenu.Update();
		return;
	}

	player.Update(fps);
	gameObjects.Update(fps);
	physics.Update(fps);
	External::camera->SyncWithTarget(fps);
	hudDisplay.Update();

	//currentCam->SetWindowPos( player->position.x, player->position.y );

	//if( player->health <= 0 )
	//{
	//	std::cout<<"Player Dead!\n";
	//}
}
		
void World::ProcessCommands()
{
	if(commands.empty())
		return;

	std::list<Command*>::iterator iter = commands.begin();
	while( iter != commands.end() )
	{
		if( (*iter)->IsComplete() )
		{
			if( (*iter)->managed == CommandType::MANAGED )
				delete *iter;
			else
				(*iter)->Reset();
			iter = commands.erase( iter );
		}
		else
		{
			(*iter)->Execute();
			if((*iter)->blocking)
				break;
			++iter;
		}
	}
}

void World::Draw()
{	
	if(loading)
	{
		DrawLoadingScreen();
		return;
	}
	
	if(inmenu)
	{
		mainmenu.Draw();
		return;
	}

	if(currentArea) //TODO: check camera and fix weird cross bug that happens when you switch between areas
		currentArea->CustomDraw();
	DrawWorldCenteredDebugCross();
	
	gameObjects.Draw();
	physics.DoDebugDraw();
	hudDisplay.Draw();
}

		
void World::AddCommand( Command* c )
{
	commands.push_back( c );
}
		
void World::AddObject( BaseObject* c )
{
	gameObjects.AddObject( c );
}
		
void World::RemoveObject( BaseObject* c )
{
	gameObjects.RemoveObject( c );
}
		
void World::Cleanup()
{
	gameObjects.Cleanup();
}


//////////////////////////////////////////////////////////////////////////////////
//////////////////////////////////////////////////////////////////////////////////
//
//////////////////////////////////////////////////////////////////////////////////

		
void World::DrawWorldCenteredDebugCross()
{	
	glColor3f(0.5,0.5,0.5);
	glBegin(GL_LINES);
	glVertex2f( -1, 0 );
	glVertex2f( 1, 0 );

	glVertex2f( 0, -1 );
	glVertex2f( 0, 1 );
	glEnd();
}


void World::DrawLoadingScreen()
{
	loadingCounter++;
	if( loadingCounter > 100 )
	{
		loadingDirection++;
		loadingDirection = loadingDirection%4;
		loadingCounter = 0;
	}

	const float distance = 1.0f;
	if( loadingDirection == 0 )
		loadingPoint.x += distance;
	else if( loadingDirection == 1 )
		loadingPoint.y += distance;
	else if( loadingDirection == 2 )
		loadingPoint.x -= distance;
	else if( loadingDirection == 3 )
		loadingPoint.y -= distance;

	glMatrixMode(GL_PROJECTION);
	glPushMatrix();
	glLoadIdentity();
	int w = glutGet(GLUT_WINDOW_WIDTH);
	int h = glutGet(GLUT_WINDOW_HEIGHT);
	gluOrtho2D(0, w, h, 0);
	glMatrixMode(GL_MODELVIEW);
	glPushMatrix();
	glLoadIdentity();

	glColor3f(0.5,0.5,0.5);
	glPointSize(3);
	glBegin(GL_POINTS);
	glVertex2f( loadingPoint.x, loadingPoint.y );
	glEnd();
	glPointSize(1);

	DebugDraw loader;
	if( loadingDirection == 0 )
		loader.DrawString(50, 50, "Loading.");
	else if( loadingDirection == 1 )
		loader.DrawString(50, 50, "Loading..");
	else if( loadingDirection == 2 )
		loader.DrawString(50, 50, "Loading...");
	else if( loadingDirection == 3 )
		loader.DrawString(50, 50, "Loading....");
	

	glPopMatrix();
	glMatrixMode(GL_PROJECTION);
	glPopMatrix();
	glMatrixMode(GL_MODELVIEW);
}